#include "StdAfx.h"
#include "MemLeak.h"
#include "TString.h"
#include <crtdbg.h>
#include <Psapi.h>
#include <time.h>
#include <fstream>
#include <tchar.h>
#include <assert.h>

#ifdef _DEBUG

#define _CRTBLD
//#include <..\..\crt\src\dbgint.h>
#include "D:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\crt\\src\\dbgint.h"

#pragma comment(lib, "Psapi.lib")
#pragma comment(lib, "imagehlp.lib")
#pragma comment(lib, "Dbghelp.lib")

#define MLD_TRACEINFO_EMPTY			_T("")
#define MLD_TRACEINFO_NOSYMBOL		_T("?(?)")


CMemLeak::CMemLeak()
: m_dwOccurance(0)
, m_bSelfAlloc(false)
, m_bLoopAlloc(false)
, m_VirtualAllocIoccurance(0)
{

}

CMemLeak::~CMemLeak()
{

}

void SymbolPaths(__in_opt TCHAR* lpszSymbolPath = NULL)
{
	TCHAR lpszPath[MAX_PATH];
	_tcscpy_s(lpszSymbolPath, MAX_PATH, _T(".;..\\;..\\..\\"));

	if ( GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), lpszPath, MAX_PATH ) ) {
		_tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
		_tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
	}

	if ( GetEnvironmentVariable( _T("_NT_ALTERNATE_SYMBOL_PATH"), lpszPath, MAX_PATH ) ) {
		_tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
		_tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
	}

	if ( GetEnvironmentVariable( _T("SYSTEMROOT"), lpszPath, MAX_PATH ) ) {
		_tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));
		_tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath);
		_tcscat_s( lpszSymbolPath, MAX_PATH, _T(";"));

		_tcscat_s( lpszSymbolPath, MAX_PATH, lpszPath );
		_tcscat_s( lpszSymbolPath, MAX_PATH, _T("\\System32"));
	}
}

#ifdef UNICODE
#define SymInitializeT SymInitializeW
#else
#define SymInitializeT SymInitialize
#endif

BOOL CMemLeak::InitSymInfo(__in_opt TCHAR* lpszUserSymbolPath)
{
	TCHAR lpszSymbolPath[MAX_PATH] = { 0 };
	DWORD symOptions = SymGetOptions();

	symOptions |= SYMOPT_LOAD_LINES; 
	symOptions &= ~SYMOPT_UNDNAME;
	SymSetOptions(symOptions);

	// Get the search path for the symbol files
	SymbolPaths(lpszSymbolPath);
	
	if ( lpszUserSymbolPath ) {
		_tcscat_s(lpszSymbolPath, MAX_PATH, _T(";"));
		_tcscat_s(lpszSymbolPath, MAX_PATH, lpszUserSymbolPath);
	}

	return SymInitializeT(GetCurrentProcess(), lpszUserSymbolPath, TRUE);
}

void CMemLeak::Init() 
{
	m_fnRtlCaptureStackBackTrace = (CaptureStackBackTraceType)(GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), "RtlCaptureStackBackTrace"));
	InitSymInfo();

	m_pfnOldCrtAllocHook = _CrtSetAllocHook( MemoryAllocHook );
}

void CMemLeak::Uninit()
{
	//dumpMemoryTrace();
	dumpCurrentMemoryTrace();
	_CrtSetAllocHook(m_pfnOldCrtAllocHook);
	//dumpCurrentVirtualAllocTrace();
	m_mapAllInfo.clear();
	m_mapAllVitualAllocBlockInfo.clear();
	SymCleanup( GetCurrentProcess() );
}

BOOL CMemLeak::symStackTrace(__out STACKFRAMEENTRY* pStacktrace)
{
	if ( m_pfnOldCrtAllocHook == NULL ) {
		return FALSE;
	}

	long StackIndex	= 0;

	void* block[MLD_MAX_TRACEINFO];
	memset(block, 0, sizeof(block));

	USHORT frames = (m_fnRtlCaptureStackBackTrace)(3, 59, (void**)block, NULL);

	for (int i = 0; i < frames; i++) {
		void* InstructionPtr = (void*)block[i];
		pStacktrace[StackIndex].addrPC.Offset	= (DWORD)InstructionPtr;
		pStacktrace[StackIndex].addrPC.Segment	= NULL;
		pStacktrace[StackIndex].addrPC.Mode		= AddrModeFlat;
		StackIndex++;
	}

	pStacktrace[StackIndex].addrPC.Offset = 0;
	pStacktrace[StackIndex].addrPC.Segment = 0;

	return TRUE;
}

void Trace(LPCTSTR lpszFormat, ...)
{
	va_list args;
	va_start(args, lpszFormat);

	int nBuf;
	TCHAR szBuffer[512];

	nBuf = _vstprintf_s(szBuffer, _countof(szBuffer), lpszFormat, args); 

	// was there an error? was the expanded string too long?
	_ASSERT(nBuf >= 0);

	OutputDebugString(szBuffer);

	va_end(args);
}

int __cdecl CMemLeak::MemoryAllocHook (
	int	allocType, 
	void	*userData, 
	size_t size, 
	int	blockType, 
	long	requestNumber, 
	const unsigned char	*filename,
	int	lineNumber
	)
{
	_CrtMemBlockHeader *pCrtHead;
	long prevRequestNumber;

	// internal C library internal allocations
	if ( blockType == _CRT_BLOCK ) {
		return TRUE;
	}

	// check if someone has turned off mem tracing
	if  ((( _CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && 
		(( allocType			== _HOOK_ALLOC)		|| 
		( allocType			== _HOOK_REALLOC))) {
			if ( Instance().m_pfnOldCrtAllocHook ) {
				Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
			}
			return TRUE;
	}

	// protect internal mem trace allocs
	if ( Instance().m_bSelfAlloc ) {
		if (Instance().m_pfnOldCrtAllocHook) {
			Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
		}
		return TRUE;
	}

	Instance().m_bSelfAlloc = true;

	switch ( allocType )
	{
	case _HOOK_ALLOC:
		{
			void* pAddr = (void *)requestNumber;
			if (CMemLeak::Instance().m_mapAllInfo.find(pAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
				Trace(_T("ERROR!MemoryAllocHook:_HOOK_ALLOC Address(0x%p) already allocated\n"), pAddr);
				return TRUE;
			}

			AllocBlockInfo ainfo;
			ainfo.address		= pAddr;
			ainfo.lineNumber	= lineNumber;
			ainfo.size			= size;
			ainfo.occurance		= Instance().m_dwOccurance++;

			Instance().symStackTrace(ainfo.traceinfo);

			if ( filename ) {
				_tcsncpy_s(ainfo.fileName, MAX_PATH, TString((const char*)filename).Data(), MAX_PATH);
			}

			if (!Instance().m_bLoopAlloc)
				CMemLeak::Instance().m_mapAllInfo[pAddr] = ainfo;
		}
		break;

	case _HOOK_REALLOC:
		{
			if (_CrtIsValidHeapPointer(userData))
			{
				pCrtHead = pHdr(userData);
				prevRequestNumber = pCrtHead->lRequest;
				//
				if (pCrtHead->nBlockUse == _IGNORE_BLOCK)
				{
					if (Instance().m_pfnOldCrtAllocHook)
					{
						Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
					}
				}

				void* pAddr = (void *)requestNumber;
				void* pOldAddr = (void *)prevRequestNumber;
				if (CMemLeak::Instance().m_mapAllInfo.find(pOldAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
					CMemLeak::Instance().m_mapAllInfo.erase(pOldAddr);
				}
				else {
					Trace(_T("ERROR!MemoryAllocHook:_HOOK_REALLOC didn't find Address(0x%08X) to free\n"), pOldAddr);
				}

				AllocBlockInfo ainfo;
				ainfo.address		= pAddr;
				ainfo.lineNumber	= lineNumber;
				ainfo.size			= size;
				ainfo.occurance		= Instance().m_dwOccurance++;

				Instance().symStackTrace(ainfo.traceinfo);

				if ( filename ) {
					_tcsncpy_s(ainfo.fileName, MAX_PATH, TString((const char*)filename).Data(), MAX_PATH);
				}

				if (!Instance().m_bLoopAlloc)
					CMemLeak::Instance().m_mapAllInfo[pAddr] = ainfo;
			}
		}
		break;

	case _HOOK_FREE:
		{
			if (_CrtIsValidHeapPointer(userData))
			{
				pCrtHead = pHdr(userData);
				requestNumber = pCrtHead->lRequest;
				//
				if (pCrtHead->nBlockUse == _IGNORE_BLOCK) {
					if (Instance().m_pfnOldCrtAllocHook) {
						Instance().m_pfnOldCrtAllocHook(allocType, userData, size, blockType, requestNumber, filename, lineNumber);
					}
				}

				AllocBlockInfo ainfo;

				void* pAddr = (void *)requestNumber;
				if (CMemLeak::Instance().m_mapAllInfo.find(pAddr) != CMemLeak::Instance().m_mapAllInfo.end()) {
					CMemLeak::Instance().m_mapAllInfo.erase(pAddr);
				}
				else {
					Trace(_T("ERROR!MemoryAllocHook:_HOOK_FREE didn't find Address(0x%08X) to free\n"), pAddr);
				}
			}
		}
		break;
	}

	Instance().m_bSelfAlloc = false;
	return TRUE;
}

void CMemLeak::DeleteOldTempFiles(const TCHAR* dir, const TCHAR* type, int days)
{
	union tu {
		FILETIME fileTime;
		ULARGE_INTEGER ul;
	};	// Seems simplest way to do the Win32 time manipulation.

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;

	TCHAR curdir[MAX_PATH];
	GetCurrentDirectory(MAX_PATH, curdir);
	SetCurrentDirectory(dir);

	hFind = FindFirstFile(type, &FindFileData);

	if ( hFind != INVALID_HANDLE_VALUE ) {

		SYSTEMTIME st;
		tu ft;

		GetSystemTime(&st);
		SystemTimeToFileTime(&st, &ft.fileTime);

		while ( FindNextFile(hFind, &FindFileData) != 0 ) {

			if (FILE_ATTRIBUTE_DIRECTORY != FindFileData.dwFileAttributes) {
				tu t;
				t.fileTime = FindFileData.ftCreationTime;

				__int64 delta = (ft.ul.QuadPart - t.ul.QuadPart) / 10000000;	// Seconds.
				int ddays = (int)(delta /= (24 * 3600));

				if ( ddays >= days ) {
					DeleteFile(FindFileData.cFileName);
				}
			}
		}
		FindClose(hFind);
	}
	SetCurrentDirectory(curdir);
}

BOOL CMemLeak::symFunctionInfoFromAddresses (
	__in ULONG_PTR fnAddress, 
	__in ULONG_PTR stackAddress, 
	__out_ecount(dwSymbolSize) TCHAR* lpszSymbol,
	__in DWORD dwSymbolSize
	)
{
	const DWORD dwBuffSize = sizeof(IMAGEHLP_SYMBOL)+sizeof(TCHAR)*1024;
	BYTE bBuff[dwBuffSize];
	::ZeroMemory(bBuff, dwBuffSize);

	PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)bBuff;
	pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
	pSym->MaxNameLength	= 1024;

	// Set the default to unknown
//	_tcscpy_s(lpszSymbol, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
	_sntprintf_s(lpszSymbol, MAX_PATH, _TRUNCATE, MLD_TRACEINFO_NOSYMBOL);

	// Get symbol info for address
#ifdef _WIN64
	DWORD64 dwDisp = 0;
#else
	DWORD dwDisp = 0;
#endif	
	if ( SymGetSymFromAddr(GetCurrentProcess(), fnAddress, &dwDisp, (PIMAGEHLP_SYMBOL)pSym) ) {

		//_tcscpy_s(lpszSymbol, dwSymbolSize, TString(pSym->Name).Data());
		_sntprintf_s(lpszSymbol, dwSymbolSize, _TRUNCATE, TString(pSym->Name).Data());
		return TRUE;
	}

	//create the symbol using the address because we have no symbol
	//_stprintf_s(lpszSymbol, dwSymbolSize, _T("0x%08X"), fnAddress);
	_sntprintf_s(lpszSymbol, dwSymbolSize, _TRUNCATE, _T("0x%08X"), fnAddress);
	return FALSE;
}

#ifdef UNICODE
	typedef IMAGEHLP_MODULEW IMAGEHLP_MODULET;
#define SymGetModuleInfoT SymGetModuleInfoW
#else
	typedef IMAGEHLP_MODULE IMAGEHLP_MODULET;
#define SymGetModuleInfoT SymGetModuleInfo
#endif

BOOL CMemLeak::symModuleNameFromAddress (
	__in ULONG_PTR address, 
	__out_ecount(dwModuleSize) TCHAR* lpszModule, 
	__in DWORD dwModuleSize)
{
	IMAGEHLP_MODULET moduleInfo;
	::ZeroMemory(&moduleInfo, sizeof(IMAGEHLP_MODULET));
	moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULET);

	if (! SymGetModuleInfoT(GetCurrentProcess(), address, &moduleInfo) ) {
		//_tcscpy_s(lpszModule, dwModuleSize, MLD_TRACEINFO_NOSYMBOL);
		_sntprintf_s(lpszModule, dwModuleSize, _TRUNCATE, MLD_TRACEINFO_NOSYMBOL);
		return FALSE;
	}

	//_tcscpy_s(lpszModule, dwModuleSize, moduleInfo.ModuleName);
	_sntprintf_s(lpszModule, dwModuleSize, _TRUNCATE, moduleInfo.ModuleName);
	return TRUE;
}

BOOL CMemLeak::symSourceInfoFromAddress(
	__in ULONG_PTR address, 
	__out_ecount(dwSourceInfoSize) TCHAR* lpszSourceInfo, 
	__in DWORD dwSourceInfoSize
	)
{
	BOOL           ret = FALSE;
	IMAGEHLP_LINE  lineInfo;
	DWORD          dwDisp;
	TCHAR          lpModuleInfo[MAX_PATH] = { 0 };

	_tcscpy_s(lpszSourceInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

	memset(&lineInfo, 0, sizeof( IMAGEHLP_LINE ));
	lineInfo.SizeOfStruct = sizeof( IMAGEHLP_LINE );

	if ( SymGetLineFromAddr(GetCurrentProcess(), address, &dwDisp, &lineInfo) ) {
		_stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("%s(%d): 0x%08X"), TString(lineInfo.FileName).Data(), lineInfo.LineNumber, address);
		ret = TRUE;
	}
	else {
		symModuleNameFromAddress(address, lpModuleInfo, sizeof(lpModuleInfo)/sizeof(lpModuleInfo[0]));

		if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0') ) {
			_stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("0x%p"), lpModuleInfo, address);	// Tim ???
		}
		else {
			_stprintf_s(lpszSourceInfo, dwSourceInfoSize, _T("%sdll! 0x%08X"), lpModuleInfo, address);
		}
		ret = FALSE;
	}

	return ret;
}

void CMemLeak::MarkMemleakReported()
{
	for (auto& item : m_mapAllInfo)
	{
		item.second.bMarkedReported = true;
	}
}

void CMemLeak::dumpCurrentMemoryTrace(int iSnapLoopSecTime)
{
	if (iSnapLoopSecTime > 0)
	{
		OutputDebugStringA("lzlong dumpCurrentMemoryTrace start sleep");
		m_bLoopAlloc = true;
		Sleep(iSnapLoopSecTime * 1000);
		OutputDebugStringA("lzlong dumpCurrentMemoryTrace end sleep");
	}
	m_bSelfAlloc = true;

	_CrtSetAllocHook(m_pfnOldCrtAllocHook);

	TCHAR				buf[2 * MAX_PATH];
	TCHAR				fileName[MAX_PATH];
	TCHAR				symInfo[MAX_PATH];
	TCHAR				srcInfo[MAX_PATH];
	TCHAR				moduleInfo[MAX_PATH];

	STACKFRAMEENTRY*	p = 0;

	ofstream myfile;

	struct tm timeinfo;
	__time64_t long_time;
	_time64(&long_time);
	// Convert to local time.
	_localtime64_s(&timeinfo, &long_time);

	TCHAR TempDir[MAX_PATH];
	TCHAR ProcName[MAX_PATH];
	GetTempPath(MAX_PATH, TempDir);
	ProcName[0] = _T('\0');
	GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName) / sizeof(TCHAR));

	_stprintf_s(fileName, MAX_PATH, _T("%smlcurrentdetector-(%s)_"), TempDir, ProcName);
	_tcsftime(buf, 2 * MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"), &timeinfo);
	_tcscat_s(fileName, MAX_PATH, buf);

	myfile.open(fileName);

	DeleteOldTempFiles(TempDir, _T("mlcurrentdetector-(*.log"), 1);

	_tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
	//_tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

	map<LPVOID, AllocBlockInfo>::iterator it = m_mapAllInfo.begin();
	for (; it != m_mapAllInfo.end(); it++)
	{
		if (it->second.bMarkedReported)
			continue;

		__int64 PcOffset = 0;
		STACKFRAMEENTRY* p1 = 0;
		p1 = &it->second.traceinfo[0];
		while (p1[0].addrPC.Offset)
		{
			PcOffset += p1[0].addrPC.Offset;
			p1++;
		}

		std::string strStack;
		if (m_mapTotalAlloc.find(PcOffset) == m_mapTotalAlloc.end())
		{
			p = &it->second.traceinfo[0];
			while (p[0].addrPC.Offset)
			{
				symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
				symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo) / sizeof(srcInfo[0]));
				symModuleNameFromAddress(p[0].addrPC.Offset, moduleInfo, sizeof(moduleInfo) / sizeof(moduleInfo[0]));

				_stprintf_s(buf, 2 * MAX_PATH, _T("Module:%s %s->%s()\n"), moduleInfo, srcInfo, symInfo);
				Trace(_T("%s->%s()\n"), srcInfo, symInfo);

				strStack += TStringA(buf).Data();
				p++;
			}
		}

		if (strStack.empty())
		{
			m_mapTotalAlloc[PcOffset].dwAllocTimes++;
			m_mapTotalAlloc[PcOffset].dwTotalsize += it->second.size;
		}
		else
		{
			m_mapTotalAlloc.insert(std::pair<__int64, AllocTotalInfo>(PcOffset, AllocTotalInfo(1, PcOffset, it->second.size, strStack)));
			//m_mapTotalAlloc[PcOffset].dwTotalsize = it->second.size;
			//m_mapTotalAlloc[PcOffset].PcOffset = PcOffset;
			//m_mapTotalAlloc[PcOffset].dwAllocTimes = 1;
			//m_mapTotalAlloc[PcOffset].strStack = strStack;
		}
	}
	
	__int64 totalAllocSize = 0;
	std::multimap<DWORD, tagAllocTotalInfo*> mapAllocTrace;
	for (auto& item : m_mapTotalAlloc)
	{
		if (item.second.strStack.find("QOpenGLFunctions_3_0") == std::string::npos &&
			item.second.strStack.find("QAEXPAVQEventTransition") == std::string::npos && 
			item.second.strStack.find("AVQRect@@ABVQModelIndex@@@Z") == std::string::npos &&
			item.second.strStack.find("QAEXPAVQEventTransition") == std::string::npos && 
			item.second.strStack.find("->CrashReport::") == std::string::npos && 
			item.second.strStack.find("->BLCLTTransactionHandler::BLCLTTransactionHandler()") == std::string::npos &&
			item.second.strStack.find("CMemLeak::VirtualAllocCallsTrace") == std::string::npos && 
			item.second.strStack.find("->BLCLTCorrestpondent::") == std::string::npos
			)
		{
			mapAllocTrace.insert(std::pair<DWORD, tagAllocTotalInfo*>(item.second.dwTotalsize, &item.second));
			totalAllocSize += item.second.dwTotalsize;
		}
	}

	for (auto& item : mapAllocTrace)
	{
		_stprintf_s(buf, 2 * MAX_PATH, _T("\n----------------------------------------------------------- total allocSize:%d   allocTimes:%d  pcOffset:%llu\n"), item.first, item.second->dwAllocTimes, item.second->PcOffset);
		Trace(buf);
		myfile << TStringA(buf).Data();

		myfile << item.second->strStack;
	}

	_stprintf_s(buf, 2 * MAX_PATH, _T("\n----------------------------------------------------------- process all total allocSize:%llu \n"), totalAllocSize);
	myfile << TStringA(buf).Data();

	myfile.close();
	m_mapTotalAlloc.clear();

	m_pfnOldCrtAllocHook = _CrtSetAllocHook(MemoryAllocHook);
	Instance().m_bSelfAlloc = false;
	Instance().m_bLoopAlloc = false;
}

void CMemLeak::dumpCurrentVirtualAllocTrace()
{
	TCHAR				buf[2 * MAX_PATH];
	TCHAR				fileName[MAX_PATH];
	TCHAR				symInfo[MAX_PATH];
	TCHAR				srcInfo[MAX_PATH];
	TCHAR				moduleInfo[MAX_PATH];

	size_t				totalSize = 0;
	int					numLeaks = 0;
	STACKFRAMEENTRY*	p = 0;

	ofstream myfile;

	struct tm timeinfo;
	__time64_t long_time;
	_time64(&long_time);
	// Convert to local time.
	_localtime64_s(&timeinfo, &long_time);

	TCHAR TempDir[MAX_PATH];
	TCHAR ProcName[MAX_PATH];
	GetTempPath(MAX_PATH, TempDir);
	ProcName[0] = _T('\0');
	GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName) / sizeof(TCHAR));

	_stprintf_s(fileName, MAX_PATH, _T("%smlCurrentVirtualAllocdetector-(%s)_"), TempDir, ProcName);
	_tcsftime(buf, 2 * MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"), &timeinfo);
	_tcscat_s(fileName, MAX_PATH, buf);

	myfile.open(fileName);

	DeleteOldTempFiles(TempDir, _T("smlCurrentVirtualAllocdetector-(*.log"), 1);

	_tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
	_tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

	map<LPVOID, AllocBlockInfo>::iterator it = m_mapAllVitualAllocBlockInfo.begin();
	for (; it != m_mapAllVitualAllocBlockInfo.end(); it++)
	{
		numLeaks++;
		_stprintf_s(buf, MAX_PATH, _T("Memory Leak(%d)------------------->\n"), numLeaks);
		Trace(buf);

		myfile << TStringA(buf).Data();

		if (_tcslen(it->second.fileName) != 0)
		{
			_stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d) %s(%d)\n"),
				it->second.address, it->second.size, it->second.occurance, it->second.fileName, it->second.lineNumber);
		}
		else
		{
			_stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d)\n"),
				it->second.address, it->second.size, it->second.occurance);
		}

		Trace(buf);
		myfile << TStringA(buf).Data();

		p = &it->second.traceinfo[0];
		while (p[0].addrPC.Offset)
		{
			symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
			symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo) / sizeof(srcInfo[0]));
			symModuleNameFromAddress(p[0].addrPC.Offset, moduleInfo, sizeof(moduleInfo) / sizeof(moduleInfo[0]));
			_stprintf_s(buf, 2 * MAX_PATH, _T("Module:%s %s->%s()\n"), moduleInfo, srcInfo, symInfo);
			Trace(_T("%s->%s()\n"), srcInfo, symInfo);

			myfile << TStringA(buf).Data();

			p++;
		}
		totalSize += it->second.size;
	}
	_stprintf_s(buf, 2 * MAX_PATH, _T("\n-----------------------------------------------------------\n"));
	Trace(buf);

	myfile << TStringA(buf).Data();

	if (!totalSize)
	{
		_stprintf_s(buf, 2 * MAX_PATH, _T("No Memory Leaks Detected for Allocations\n\n"));
		Trace(buf);
		myfile << TStringA(buf).Data();
	}
	else
	{
		_stprintf_s(buf, 2 * MAX_PATH, _T("Total %d Memory Leaks: %d bytes Total Alocations %d\n\n"), numLeaks, totalSize, m_dwOccurance);
	}

	Trace(buf);

	myfile << TStringA(buf).Data();

#ifdef UNICODE
	const TCHAR *umb = _T("Unicode");
#else
	const TCHAR *umb = _T("Multibyte");
#endif

#ifdef _WIN64
	const TCHAR *w64 = _T("64 bit");
#else
	const TCHAR *w64 = _T("32 bit");
#endif

#ifdef NDEBUG
	const TCHAR *dbg = _T("release build.");
#else
	const TCHAR *dbg = _T("debug build.");
#endif

	_stprintf_s(TempDir, MAX_PATH, _T("%s %s %s\n"), umb, w64, dbg);

	myfile << TStringA(TempDir).Data();
	Trace(TempDir);

	myfile.close();
	m_VirtualAllocIoccurance = 0;
}

void CMemLeak::VirtualAllocCallsTrace(LPVOID lpAllocAddr, DWORD dwSize)
{
	AllocBlockInfo ainfo;
	ainfo.address = lpAllocAddr;
	ainfo.lineNumber = 0;
	ainfo.size = dwSize;
	ainfo.occurance = m_VirtualAllocIoccurance++;
	Instance().symStackTrace(ainfo.traceinfo);

	m_mapAllVitualAllocBlockInfo[lpAllocAddr] = ainfo;
}

void CMemLeak::VirtualFreeCallsTrace(LPVOID lpAllocAddr)
{
	if (m_mapAllVitualAllocBlockInfo.find(lpAllocAddr) != m_mapAllVitualAllocBlockInfo.end()) {
		m_mapAllVitualAllocBlockInfo.erase(lpAllocAddr);
		m_VirtualAllocIoccurance--;
	}
	else{
		Trace(_T("ERROR!CMemLeak:VirtualFreeCallsTrace didn't find Address(0x%08X) to free\n"), lpAllocAddr);
	}
}

void CMemLeak::dumpMemoryTrace()
{
	TCHAR				buf[2*MAX_PATH];
	TCHAR				fileName[MAX_PATH];
	TCHAR				symInfo[MAX_PATH];
	TCHAR				srcInfo[MAX_PATH];

	size_t				totalSize						= 0;
	int					numLeaks						= 0;
	STACKFRAMEENTRY*	p								= 0;

	ofstream myfile;

	struct tm timeinfo;
	__time64_t long_time;
	_time64(&long_time);
	// Convert to local time.
	_localtime64_s(&timeinfo, &long_time);

	TCHAR TempDir[MAX_PATH];
	TCHAR ProcName[MAX_PATH];
	GetTempPath(MAX_PATH, TempDir);
	ProcName[0] = _T('\0');
	GetModuleBaseName(GetCurrentProcess(), NULL, ProcName, sizeof(ProcName)/sizeof(TCHAR));

	_stprintf_s(fileName, MAX_PATH, _T("%smldetector-(%s)_"), TempDir, ProcName); 
	_tcsftime(buf,2*MAX_PATH, _T("%b%d-%Y__%H-%M-%S.log"),&timeinfo);
	_tcscat_s(fileName,MAX_PATH, buf);

	myfile.open(fileName); 

	DeleteOldTempFiles(TempDir, _T("mldetector-(*.log"), 1);

	_tcscpy_s(symInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);
	_tcscpy_s(srcInfo, MAX_PATH, MLD_TRACEINFO_NOSYMBOL);

	map<LPVOID, AllocBlockInfo>::iterator it = m_mapAllInfo.begin();
	for (; it != m_mapAllInfo.end(); it++)
	{
		numLeaks++;
		_stprintf_s(buf, MAX_PATH, _T("Memory Leak(%d)------------------->\n"), numLeaks);
		Trace(buf);

		myfile << TStringA(buf).Data();

		if (_tcslen(it->second.fileName) != 0)
		{
			_stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d) %s(%d)\n"), 
				it->second.address, it->second.size, it->second.occurance, it->second.fileName, it->second.lineNumber);
		}
		else
		{
			_stprintf_s(buf, MAX_PATH, _T("Memory Leak <0x%p> bytes(%d) occurance(%d)\n"), 
				it->second.address, it->second.size, it->second.occurance);
		}

		Trace(buf);
		myfile << TStringA(buf).Data();

		p = &it->second.traceinfo[0];
		while(p[0].addrPC.Offset)
		{
			symFunctionInfoFromAddresses(p[0].addrPC.Offset, p[0].addrFrame.Offset, symInfo, MAX_PATH);
			symSourceInfoFromAddress(p[0].addrPC.Offset, srcInfo, sizeof(srcInfo)/sizeof(srcInfo[0]));
			_stprintf_s(buf, 2*MAX_PATH, _T("%s->%s()\n"), srcInfo, symInfo);
			Trace(_T("%s->%s()\n"), srcInfo, symInfo);

			myfile << TStringA(buf).Data();

			p++;
		}
		totalSize += it->second.size;
	}
	_stprintf_s(buf, 2*MAX_PATH, _T("\n-----------------------------------------------------------\n"));
	Trace(buf);

	myfile << TStringA(buf).Data();

	if(!totalSize) 
	{
		_stprintf_s(buf, 2*MAX_PATH, _T("No Memory Leaks Detected for %d Allocations\n\n"), m_dwOccurance);
		Trace(buf);
		myfile << TStringA(buf).Data();
	}
	else
	{
		_stprintf_s(buf, 2*MAX_PATH, _T("Total %d Memory Leaks: %d bytes Total Alocations %d\n\n"), numLeaks, totalSize, m_dwOccurance);
	}

	Trace(buf);

	myfile << TStringA(buf).Data();

#ifdef UNICODE
	const TCHAR *umb = _T("Unicode");
#else
	const TCHAR *umb = _T("Multibyte");
#endif

#ifdef _WIN64
	const TCHAR *w64 = _T("64 bit");
#else
	const TCHAR *w64 = _T("32 bit");
#endif

#ifdef NDEBUG
	const TCHAR *dbg = _T("release build.");
#else
	const TCHAR *dbg = _T("debug build.");
#endif

	_stprintf_s(TempDir, MAX_PATH, _T("%s %s %s\n"), umb, w64, dbg);

	myfile << TStringA(TempDir).Data();
	Trace(TempDir);

	myfile.close();
}

#endif // _DEBUG